from error import *
from tokens import *
from ast_node import *


"""
解释器输出结果
"""
class RTResult(object):
    """
    value: Number 类型, 四则运算器输出结果
    error: 如果发生错误, 该值不为 None
    """
    def __init__(self):
        self.value: Number = None
        self.error: RunTimeError = None

    def success(self, value):
        self.value = value
        return self

    def failure(self, error):
        self.error = error
        return self

    def register(self, res):
        """
        传递错误给根递归
        :param res:
        :return:
        """
        if res.error:
            self.error = res.error
        return res.value

"""
扩展数字类型, 用于包裹解释器的输出值
"""
class Number(object):
    def __init__(self, value):
        self.context = None
        self.pos_end = None
        self.pos_start = None
        self.value = value
        self.set_pos()  # 报错的位置
        self.set_context()  # 方便定义错误, 报错的上下文

    def set_pos(self, pos_start: Position = None, pos_end: Position = None) -> 'Number':
        self.pos_start = pos_start
        self.pos_end = pos_end
        return self

    def set_context(self, context=None) -> 'Number':
        self.context = context
        return self

    def added_by(self, other: 'Number') -> ('Number', Error):
        """
        加法
        :param other:
        :return:
        """
        if isinstance(other, Number):
            return Number(self.value + other.value).set_context(self.context), None

    def subbed_by(self, other: 'Number') -> ('Number', Error):
        """
        减法
        :param other:
        :return:
        """
        if isinstance(other, Number):
            return Number(self.value - other.value).set_context(self.context), None

    def multed_by(self, other: 'Number') -> ('Number', Error):
        """
        乘法
        :param other:
        :return:
        """
        if isinstance(other, Number):
            return Number(self.value * other.value).set_context(self.context), None

    def dived_by(self, other: 'Number') -> ('Number', Error):
        """
        除法
        :param other:
        :return:
        """
        if isinstance(other, Number):
            if other.value == 0:
                return None, RunTimeError(other.pos_start, other.pos_end, '分母不可为0', self.context)
            return Number(self.value / other.value).set_context(self.context), None

    def __repr__(self):
        return str(self.value)


"""
Context 上下文, 用于找到报错的位置, 不重要
"""

class Context(object):
    def __init__(self, display_name, parent=None, parent_entry_pos=None):
        """
        :param display_name: 展示的名字
        :param parent:  父上下文, 形成上下文栈
        :param parent_entry_pos:  父位置
        """
        self.display_name = display_name
        self.parent = parent
        self.parent_entry_pos = parent_entry_pos


"""
解释器
"""
class Interpreter(object):
    def visit(self, node, context: Context) -> RTResult:
        """
        递归下降算法, 遍历 ast node
        :param node: AST Node
        :param context: 上下文对象
        :return:
        """
        # method_name = f'visit_{type(node).__name__}'
        # method = getattr(self, method_name, self.no_visit_method)
        if isinstance(node, NumberNode):
            return self.visitNumberNode(node, context)
        elif isinstance(node, BinOpNode):
            return self.visitBinOpNode(node, context)
        elif isinstance(node, UnaryOpNode):
            return self.visitUnaryOpNode(node, context)
        else:
            return self.no_visit_method(node)

    def no_visit_method(self, node):
        raise Exception(f'No visit_{type(node).__name}')

    def visitNumberNode(self, node, context) -> RTResult:
        """若当前AST节点为数字节点, 处理方法"""
        return RTResult().success(
            Number(node.token.value).set_context(context).set_pos(node.pos_start, node.pos_end)
        )

    def visitBinOpNode(self, node, context) -> RTResult:
        """若当前AST节点为二元运算符节点, 处理方法"""
        res = RTResult()
        # 左递归
        left = res.register(self.visit(node.left_node, context))
        if res.error:
            return res
        # 右递归
        right = res.register(self.visit(node.right_node, context))
        if res.error:
            return res
        error = None
        result = None
        if node.op_token.type == TT_PLUS:  # 加法
            result, error = left.added_by(right)
        elif node.op_token.type == TT_MINUS:  # 减法
            result, error = left.subbed_by(right)
        elif node.op_token.type == TT_MUL:  # 乘法
            result, error = left.multed_by(right)
        elif node.op_token.type == TT_DIV:  # 除法
            result, error = left.dived_by(right)

        if error:
            return res.failure(error)
        else:
            return res.success(result.set_pos(node.pos_start, node.pos_end))

    def visitUnaryOpNode(self, node, context) -> RTResult:
        """若当前AST节点为一元运算符节点, 处理方法"""
        res = RTResult()
        number = res.register(self.visit(node.node, context))
        if res.error:
            return res

        error = None

        if node.op_token.type == TT_MINUS:  # 符号
            number, error = number.multed_by(Number(-1))
        if error:
            return res.failure(error)
        else:
            return res.success(number.set_pos(node.pos_start, node.pos_end))
